﻿using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows.Forms;

#pragma warning disable CA1416 // 验证平台兼容性
namespace HookINCS
{
    /********** 使用方法 ********
     * 
     //hook 修改弹出窗按钮 文本
            var hook = new HookINCS.Hook();
            hook.OnMessageBoxShow += (s, mbe) =>
            {
                IntPtr hChildWnd = mbe.hChildWnd;
                int result;
                if (HookINCS.Win32Api_Hook.GetDlgItem(hChildWnd, 6) != 0)//IDYES = 6
                    {
                    result = HookINCS.Win32Api_Hook.SetDlgItemTextA(hChildWnd, 6, "保存(Y)");//在Project.Resources里自定义文本
                    }
                if (HookINCS.Win32Api_Hook.GetDlgItem(hChildWnd, 7) != 0)//IDNO = 7
                    {
                    result = HookINCS.Win32Api_Hook.SetDlgItemTextA(hChildWnd, 7, "隐藏(N)");
                    }
            };
            hook.InstallMessageBoxHook();

            //用完之后再取消钩子
            hook.UninstallMessageBoxHook();
     
         */
    /// <summary>
    /// 全局钩子类(非远程注入)
    /// 提供事件对键盘、鼠标的消息进行拦截
    /// 您可以使用事件注册的方式通过自定义方法处理键盘、鼠标的动作
    /// 值得注意的是：您必须手动安装钩子并将自定义方法注册到事件中后，您才能对事件进行拦截
    /// </summary>
    public sealed class Hook
    {
        #region 单例实现相关
        //static Hook() { }
        //private Hook() { }

        ///// <summary>
        ///// 内部类，用于提供Hook类的单实例
        ///// </summary>
        //private class Nested
        //{
        //    static Nested() { }
        //    internal static readonly Hook instance = new Hook();
        //}

        //~Hook()
        //{
        //    UninstallAllHook();
        //}

        //public static Hook GetInstance()
        //{
        //    return Nested.instance;
        //}
        #endregion

        #region 钩子句柄

        /// <summary>
        /// 键盘钩子句柄
        /// </summary>
        private int kbhHook = 0;

        /// <summary>
        /// 鼠标钩子
        /// </summary>
        private int mshHook = 0;

        /// <summary>
        /// 弹窗钩子
        /// </summary>
        private int msboxHook = 0;

        /// <summary>
        /// 自定义钩子
        /// </summary>
        private int userHook = 0;

        /// <summary>
        /// 键盘钩子回调函数
        /// </summary>
        private HookProc? kbHook;
        /// <summary>
        /// 鼠标钩子回调函数
        /// </summary>
        private HookProc? msHook;
        /// <summary>
        /// 弹窗钩子回调函数
        /// </summary>
        private HookProc? mboxHook;

        #endregion

        #region 提供给外界使用的事件

        /// <summary>
        /// 当键盘按键按下
        /// </summary>
        public event KeyEventHandler? OnKeyDown;

        /// <summary>
        /// 当键盘某个键松开
        /// </summary>
        public event KeyEventHandler? OnKeyUp;

        /// <summary>
        /// 当键盘某个键按下后松开
        /// </summary>
        public event KeyEventHandler? OnKeyPress;

        // 鼠标相关
        /// <summary>
        /// 当鼠标单击
        /// </summary>
        public event MouseEventHandler? OnMouseClick;

        /// <summary>
        /// 当鼠标双击
        /// </summary>
        public event MouseEventHandler? OnMouseDoubleClick;

        /// <summary>
        /// 当鼠标某个键被按下
        /// </summary>
        public event MouseEventHandler? OnMouseDown;
        //public event MouseEventHandler OnMouseEnter;
        //public event MouseEventHandler OnMouseHover;
        //public event MouseEventHandler OnMouseLeave;

        /// <summary>
        /// 当鼠标移动时(推荐具有一定经验的专业人士使用)
        /// </summary>
        public event MouseEventHandler? OnMouseMove;

        /// <summary>
        /// 当鼠标某个键松开
        /// </summary>
        public event MouseEventHandler? OnMouseUp;

        /// <summary>
        /// 定义一个委托类型的事件 
        /// </summary>
        public event EventHandler<MessageBoxEventArgs>? OnMessageBoxShow;
        public class MessageBoxEventArgs { public IntPtr hChildWnd; public MessageBoxEventArgs() { } public MessageBoxEventArgs(IntPtr _hChildWnd) { hChildWnd = _hChildWnd; } }
        #endregion

        #region 安装钩子、卸载钩子的方法

        #region ***** 基础hook *****
        /// <summary>
        /// 使用自定义配置安装钩子(不建议使用)
        /// </summary>
        /// <param name="type">钩子类型</param>
        /// <param name="dele">钩子回调函数</param>
        public int InstallHook(HookType type, HookProc dele)
        {
            if (userHook == 0)
            {
                userHook = Win32Api_Hook.SetWindowsHookEx(type, dele, Win32Api_Hook.GetModuleHandle(Process.GetCurrentProcess().MainModule!.ModuleName), 0);
                if (userHook == 0)
                    UninstallKeyBoardHook();
            }
            return userHook;
        }

        /// <summary>
        /// 更具自由性的钩子绑定方法
        /// 您几乎可以为任何线程(也可以是全部线程)、任何模块(也可以是主模块)设置任意类型的钩子
        /// </summary>
        /// <param name="type">钩子类型</param>
        /// <param name="dele">钩子回调函数</param>
        /// <param name="moduleHandel">回调函数所在模块句柄</param>
        /// <param name="threadId">线程编号(0表示为所有线程设置钩子)</param>
        public void InstallHook(HookType type, HookProc dele, IntPtr moduleHandel, int threadId)
        {
            if (userHook == 0)
            {
                userHook = Win32Api_Hook.SetWindowsHookEx(type, dele, moduleHandel, threadId);
                if (userHook == 0)
                    UninstallKeyBoardHook();
            }
        }

        /// <summary>
        /// 卸载所有钩子
        /// </summary>
        public void UninstallAllHook()
        {
            UninstallKeyBoardHook();
            UninstallMouseHook();
            UninstallHook(ref userHook);
        }

        /// <summary>
        /// 卸载钩子
        /// </summary>
        /// <param name="idhook"></param>
        private void UninstallHook(ref int idhook) { if (idhook != 0) { Win32Api_Hook.UnhookWindowsHookEx(idhook); idhook = 0; } }
        #endregion

        #region ***** 键鼠 *****

        /// <summary>
        /// 安装键盘钩子(使用默认配置)
        /// </summary>
        public void InstallKeyBoardHook() { InstallKeyBoardHook(HookType.WH_KEYBOARD_LL); }

        /// <summary>
        /// 安装鼠标钩子(使用默认配置)
        /// </summary>
        public void InstallMouseHook() { InstallMouseHook(HookType.WH_MOUSE_LL); }

        /// <summary>
        /// 安装键盘和鼠标钩子(使用默认配置)
        /// </summary>
        public void InstallMouseAndKeyBoardHook() { InstallKeyBoardHook(); InstallMouseHook(); }

        /// <summary>
        /// 使用自定义配置安装键盘钩子(推荐有一定经验的专业人士使用)
        /// </summary>
        /// <param name="type"></param>
        public void InstallKeyBoardHook(HookType type)
        {
            if (kbhHook == 0)
            {
                kbHook = new HookProc(DefaultKeyBoardHookProc);
                kbhHook = Win32Api_Hook.SetWindowsHookEx(type, kbHook, Win32Api_Hook.GetModuleHandle(Process.GetCurrentProcess().MainModule!.ModuleName), 0);
                if (kbhHook == 0)
                    UninstallKeyBoardHook();
            }
        }

        /// <summary>
        /// 使用自定义配置安装鼠标钩子(推荐有一定经验的专业人士使用)
        /// </summary>
        /// <param name="type"></param>
        public void InstallMouseHook(HookType type)
        {
            if (mshHook == 0)
            {
                msHook = new HookProc(DefaultMouseHookProc);
                mshHook = Win32Api_Hook.SetWindowsHookEx(type, msHook, Win32Api_Hook.GetModuleHandle(Process.GetCurrentProcess().MainModule!.ModuleName), 0);
                if (mshHook == 0)
                    UninstallMouseHook();
            }
        }

        /// <summary>
        /// 卸载键盘钩子
        /// </summary>
        public void UninstallKeyBoardHook() { UninstallHook(ref kbhHook); }

        /// <summary>
        /// 卸载鼠标钩子
        /// </summary>
        public void UninstallMouseHook() { UninstallHook(ref mshHook); }

        #endregion

        #region ***** 窗口 *****

        /// <summary>
        /// 安装弹窗钩子
        /// </summary>
        /// <param name="type"></param>
        public void InstallMessageBoxHook() { InstallMessageBoxHook(HookType.WH_CBT); }
        /// <summary>
        /// 安装弹窗钩子
        /// </summary>
        /// <param name="type"></param>
        public void InstallMessageBoxHook(HookType type)
        {
            if (msboxHook == 0)
            {
                mboxHook = new HookProc(DefaultMessageBoxHookProc);
                //Win32Api_Hook.GetCurrentThreadId()//自身线程，如果是0则表示全局
                msboxHook = Win32Api_Hook.SetWindowsHookEx(type, mboxHook, Win32Api_Hook.GetModuleHandle(Process.GetCurrentProcess().MainModule!.ModuleName), Win32Api_Hook.GetCurrentThreadId());
                if (msboxHook == 0)
                    UninstallMessageBoxHook();
            }
        }

        /// <summary>
        /// 卸载弹窗钩子
        /// </summary>
        public void UninstallMessageBoxHook() { UninstallHook(ref msboxHook); }

        #endregion

        #endregion

        #region 钩子发挥作用时的方法
        private int DefaultMessageBoxHookProc(int nCode, int wParam, IntPtr lParam)
        {
            IntPtr hChildWnd;// msgbox is "child"
            // notification that a window is about to be activated
            // window handle is wParam
            if (nCode == 5)//HCBT_ACTIVATE = 5  
            {
                // set window handles of messagebox
                hChildWnd = (IntPtr)wParam;
                //to get the text of yes button
                //自定义事件
                OnMessageBoxShow?.Invoke(this, new MessageBoxEventArgs(hChildWnd));

                //int result;
                //if (Win32Api_Hook.GetDlgItem(hChildWnd, 6) != 0)//IDYES = 6
                //    {
                //    result = Win32Api_Hook.SetDlgItemTextA(hChildWnd, 6, Properties.Resources.YES);//在Project.Resources里自定义文本
                //    }
                //if (Win32Api_Hook.GetDlgItem(hChildWnd, 7) != 0)//IDNO = 7
                //    {
                //    result = Win32Api_Hook.SetDlgItemTextA(hChildWnd, 7, Properties.Resources.NO);
                //    }
            }
            //return (IntPtr)1; //直接返回了，该消息就处理结束了
            return Win32Api_Hook.CallNextHookEx(msboxHook, nCode, wParam, lParam);// otherwise, continue with any possible chained hooks; //返回，让后面的程序处理该消息
        }
        /// <summary>
        /// 默认键盘钩子回调函数
        /// </summary>
        /// <param name="nCode"></param>
        /// <param name="wParam"></param>
        /// <param name="lParam"></param>
        /// <returns></returns>
        private int DefaultKeyBoardHookProc(int nCode, int wParam, IntPtr lParam)
        {
            KBDLLHOOKSTRUCT kbhs = (KBDLLHOOKSTRUCT?)Marshal.PtrToStructure(lParam, typeof(KBDLLHOOKSTRUCT)) ?? new();
            if (wParam == (int)MsgType.WM_KEYDOWN && OnKeyDown != null)
                OnKeyDown?.Invoke(this, new KeyEventArgs((Keys)kbhs.vkCode));
            else
            {
                if (wParam == (int)MsgType.WM_KEYUP && OnKeyUp != null)
                    OnKeyUp?.Invoke(this, new KeyEventArgs((Keys)kbhs.vkCode));
                if (wParam == (int)MsgType.WM_KEYUP && OnKeyPress != null)
                    OnKeyPress?.Invoke(this, new KeyEventArgs((Keys)kbhs.vkCode));
            }
            return Win32Api_Hook.CallNextHookEx(kbhHook, nCode, wParam, lParam);
        }

        /// <summary>
        /// 默认鼠标钩子回调函数
        /// </summary>
        /// <param name="nCode"></param>
        /// <param name="wParam"></param>
        /// <param name="lParam"></param>
        /// <returns></returns>
        private int DefaultMouseHookProc(int nCode, int wParam, IntPtr lParam)
        {
            MOUSEHOOKSTRUCT mshs = (MOUSEHOOKSTRUCT?)Marshal.PtrToStructure(lParam, typeof(MOUSEHOOKSTRUCT)) ?? new();

            MouseButtons button = MouseButtons.None;

            int clickCount = 0;
            switch (wParam)
            {
                case (int)MsgType.WM_LBUTTONDOWN:
                    button = MouseButtons.Left;
                    clickCount = 1;
                    InvokeMouseEvent(OnMouseDown, button, clickCount, mshs.pt);
                    break;
                case (int)MsgType.WM_LBUTTONUP:
                    button = MouseButtons.Left;
                    clickCount = 1;
                    InvokeMouseEvent(OnMouseClick, button, clickCount, mshs.pt);
                    InvokeMouseEvent(OnMouseUp, button, clickCount, mshs.pt);
                    break;
                case (int)MsgType.WM_LBUTTONDBLCLK:
                    button = MouseButtons.Left;
                    clickCount = 2;
                    InvokeMouseEvent(OnMouseDoubleClick, button, clickCount, mshs.pt);
                    break;
                case (int)MsgType.WM_RBUTTONDOWN:
                    button = MouseButtons.Right;
                    clickCount = 1;
                    InvokeMouseEvent(OnMouseDown, button, clickCount, mshs.pt);
                    break;
                case (int)MsgType.WM_RBUTTONUP:
                    button = MouseButtons.Right;
                    clickCount = 1;
                    InvokeMouseEvent(OnMouseClick, button, clickCount, mshs.pt);
                    InvokeMouseEvent(OnMouseUp, button, clickCount, mshs.pt);
                    break;
                case (int)MsgType.WM_RBUTTONDBLCLK:
                    button = MouseButtons.Right;
                    clickCount = 2;
                    InvokeMouseEvent(OnMouseDoubleClick, button, clickCount, mshs.pt);
                    break;
                case (int)MsgType.WM_MOUSEMOVE:
                    InvokeMouseEvent(OnMouseMove, button, clickCount, mshs.pt);
                    break;
            }
            return Win32Api_Hook.CallNextHookEx(mshHook, nCode, wParam, lParam);
        }

        /// <summary>
        /// 鼠标事件调用
        /// </summary>
        /// <param name="eventName">事件名</param>
        /// <param name="button">鼠标按键</param>
        /// <param name="clickCount">鼠标点击次数</param>
        /// <param name="p">鼠标坐标</param>
        private void InvokeMouseEvent(Delegate? eventName, MouseButtons button, int clickCount, POINT p)
        {
            MouseEventArgs e = new MouseEventArgs(button, clickCount, p.x, p.y, 0);
            eventName?.DynamicInvoke(this, e);
        }

        #endregion
    }
}
#pragma warning restore CA1416 // 验证平台兼容性